var log = function(eventDescription:string) {
	var logEntry:NotesDocument = database.createDocument();
	logEntry.replaceItemValue("Form", "Log Entry");
	logEntry.replaceItemValue("entryUser", context.getUser().getDistinguishedName());
	logEntry.replaceItemValue("entryDescription", eventDescription);
	logEntry.save();
};

var CachedEntry = function(unid) {
	var sharedUnid:string = "";
	var getDocument = function() {
		return database.getDocumentByUNID(unid);
	};
	var getSharedDocument = function() {
		var sharedDocument:NotesDocument;
		sharedDocument = Syndicate().getDocumentByPrimaryKey(database, getDocument().getItemValueString("entryUrl"));
		return sharedDocument;
	};
	var getSharedField = function(fieldName) {
		return getSharedDocument().getItemValueString(fieldName);
	};
	
	var cachedFeedEntry = {
		getAuthor: function() {
			return getSharedField("entryAuthor");
		},
		getCategories: function() {
			return getSharedDocument().getItemValue("entryCategories");
		},
		getCommentFeed: function() {
			return getSharedField("entryCommentFeed");
		},
		getContent: function() {
			return getSharedField("entryContent");
		},
		getFeed: function() {
			return new Feed(getSharedField("feedURL"));
		},
		getPublishDate: function() {
			return getSharedField("entryPublishDate");
		},
		getSubscription: function() {
			return Syndicate().getSubscription(getSharedField("feedURL"));
		},
		getSummary: function() {
			return getSharedField("entrySummary");
		},
		getTitle: function() {
			return getSharedField("entryTitle");
		},
		getUrl: function() {
			return getSharedField("entryURL");
		},
		isUnread: function() {
			return (!(getDocument().getRead(context.getUser().getDistinguishedName())));
		},
		markRead: function() {
			return getDocument().markRead(context.getUser().getDistinguishedName());
		}
	};
	return cachedFeedEntry;
	
};

var FeedEntry = function(element:DOMElement, feed) {
	var parentFeed = feed;
	var entryNode:DOMElement = element;
	var feedType:string = entryNode.getNodeName().toLowerCase();
	var isAtom = function(){
		return (feedType == "entry");
	};
	var isRSS = function(){
		return (feedType == "item");
	};
	
	var thisEntry = {
		cache: function() {
			try {
			var entryUrl:string = this.getUrl();
			var docCache:NotesDocument = Syndicate().getDocumentByPrimaryKey(database, entryUrl);
			var publishDate = this.getPublishDate();
			var siteUrl:string = parentFeed.getFeedUrl();
			if (docCache.isNewNote()) {
				docCache.replaceItemValue("Form", "Cached Entry");
				docCache.replaceItemValue("entryAuthor", this.getAuthor());
				docCache.replaceItemValue("entryCategories", this.getCategories());
				docCache.replaceItemValue("entryCommentFeed", this.getCommentFeed());
				docCache.replaceItemValue("entryContent", this.getContent()).setSummary(false);
				docCache.replaceItemValue("entryPublishDate", publishDate);
				docCache.replaceItemValue("entrySummary", this.getSummary());
				docCache.replaceItemValue("entryTitle", this.getTitle());
				docCache.replaceItemValue("entryURL", entryUrl);
				docCache.replaceItemValue("entryXML", this.getXMLString()).setSummary(false);
				docCache.replaceItemValue("feedURL", siteUrl);
				docCache.save(true, true, false);
			}
			var subscriber:string = context.getUser().getDistinguishedName();
			var docUserStub:NotesDocument = Syndicate().getDocumentByPrimaryKey(database, subscriber + entryUrl);
			if (docUserStub.isNewNote()) {
				docUserStub.replaceItemValue("Form", "User Entry Cache");
				docUserStub.replaceItemValue("subscriber", subscriber);
				docUserStub.replaceItemValue("entryURL", entryUrl);
				docUserStub.replaceItemValue("siteURL", siteUrl);
				docUserStub.replaceItemValue("entryCategories", this.getCategories());
				docUserStub.replaceItemValue("isFavorite", "0");
				docUserStub.replaceItemValue("isShared", "0");
				docUserStub.replaceItemValue("entryPublishDate", publishDate);
				docUserStub.save(true, true, false);
			}
			return docCache;
			} catch (e) {
				return null;
			}
		},
		getAuthor: function() {
			var postAuthor:string = "";
			if (isAtom()) {
				var authorNode:DOMElement = entryNode.getElementsByTagName("author").item(0);
				var nameNode:DOMElement = authorNode.getElementsByTagName("name").item(0);
				postAuthor = nameNode.getFirstChild().getNodeValue();
			} else if (isRSS()) {
				try {
					postAuthor = entryNode.getElementsByTagName("dc:creator").item(0).getFirstChild().getNodeValue();
				} catch (e) {
					return "Unknown Author";
				}
			}
			return postAuthor;
		},
		getCategories: function() {
			var categoryNodes:DOMNodeList = entryNode.getElementsByTagName("category");
			var categories = [];
			try {
				for (var i = 0; i < categoryNodes.getLength(); i++) {
					var category:string = "";
					var categoryNode:DOMElement = categoryNodes.item(i);
					if (isAtom()) {
						category = categoryNode.getAttribute("term");
					} else if (isRSS()) {
						category = categoryNode.getFirstChild().getNodeValue();
					}
					if (category != "") {
						categories.push(category);
					}
				}
			} catch (e) {
				return categories;
			}
			return categories;
			
		},
		getCommentFeed: function() {
			var commentFeedUrl:string = "";
			var commentFeed:DOMNodeList = entryNode.getElementsByTagName("wfw:commentRss");
			if (commentFeed.getLength() > 0) {
				commentFeedUrl = commentFeed.item(0).getFirstChild().getNodeValue();
			}
			return commentFeedUrl;
		},
		getContent: function() {
			var postContent:string = "";
			try {
				if (isAtom()) {
					postContent = entryNode.getElementsByTagName("content").item(0).getFirstChild().getNodeValue();
				} else if (isRSS()) {
					postContent = entryNode.getElementsByTagName("content:encoded").item(0).getFirstChild().getTextContent();
				}
			} catch (e) {
				// settle for a summary if post has no content
				postContent = this.getSummary();
			}
			return postContent;
		},
		getFeed: function () {
			return parentFeed;
		},
		getPublishDate: function() {
			var startIndex:java.text.ParsePosition = new java.text.ParsePosition(0);
			var dateText:string = "";
			try {
				if (isAtom()) {
					dateText = entryNode.getElementsByTagName("published").item(0).getFirstChild().getNodeValue();
				} else if (isRSS()) {
					dateText = entryNode.getElementsByTagName("pubDate").item(0).getFirstChild().getNodeValue();
				}
				try {
					dateText = session.createDateTime(dateText).getLocalTime();
				} catch(e) {
					return dateText;
				}
			} catch (e) {
				return session.createDateTime(@Now()).getLocalTime();
			}
			return dateText;
		},
		getSummary: function() {
			var postSummary:string = "";
			try {
				if (isAtom()) {
					postSummary = entryNode.getElementsByTagName("summary").item(0).getFirstChild().getNodeValue();
				} else if (isRSS()) {
					postSummary = entryNode.getElementsByTagName("description").item(0).getFirstChild().getNodeValue();
				}
			} catch (e) {
				return postSummary;
			}
			return postSummary;
		},
		getTitle: function() {
			var title:string;
			try {
				title = entryNode.getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
			} catch (e) {
				log(e.toString());
				title = e.toString();
			}
			return title;
		},
		getUrl: function() {
			var entryUrl:string;
			var links:DOMNodeList = entryNode.getElementsByTagName("link");
			if (isAtom()) {
				for (var i = 0; i < links.getLength(); i++) {
					var thisLink:DOMElement = links.item(i);
					if (thisLink.getAttribute("rel") == "alternate") {
						entryUrl = thisLink.getAttribute("href");
					}
				}
			} else if(isRSS()) {
				entryUrl = links.item(0).getFirstChild().getNodeValue();
			}
			return entryUrl;
		},
		getXMLString: function() {
			return entryNode.getXMLString();
		}
	};

	return thisEntry;
};

var Feed = function(url:string){
	var feedCacheKey:string = "feedcache-" + url.toLowerCase();
	var parsed:boolean = false;
	var feedUrl:string = url;
	var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
	var parser = factory.newDocumentBuilder();
	
	if(!(viewScope.containsKey(feedCacheKey))) {
		viewScope.put(feedCacheKey, parser.parse(url));
	}
	
	var feedDom:DOMDocument = viewScope.get(feedCacheKey);
	var feedRoot:DOMElement = feedDom.getDocumentElement();
	var feedType:string = feedRoot.getNodeName().toLowerCase();
	var isAtom = function(){
		return (feedType == "feed");
	};
	var isRSS = function(){
		return (feedType == "rss");
	};
	
	return {
		cacheEntries: function() {
			var entries = this.getEntries();
			for (var i = 0; i < entries.length; i++) {
				entries[i].cache();
			}
		},
		getChannel: function() {
			var channelElement:DOMElement = null;
			if (isAtom()) {
				channelElement = feedRoot;
			} else if (isRSS()) {
				channelElement = feedDom.getElementsByTagName("channel").item(0);
			}
			return channelElement;
		},
		getDom: function() {
			return feedDom;
		},
		getEntries: function() {
			var entryTagName:string;
			if (isAtom()) {
				entryTagName = "entry";
			} else if(isRSS()) {
				entryTagName = "item";
			}
			var entries:DOMNodeList = this.getChannel().getElementsByTagName(entryTagName);
			var entryArray = [];
			for (var i = 0; i < entries.getLength(); i++){
				entryArray.push(new FeedEntry(entries.item(i), this));
			}
			return entryArray;
		},
		getTitle: function() {
			return this.getChannel().getElementsByTagName("title").item(0).getFirstChild().getNodeValue();
		},
		getFeedUrl: function() {
			return feedUrl;
		},
		getSiteUrl: function() {
			var siteUrl:string;
			var links:DOMNodeList = this.getChannel().getElementsByTagName("link");
			if (isAtom()) {
				for (var i = 0; i < links.getLength(); i++) {
					var thisLink:DOMElement = links.item(i);
					if (thisLink.getAttribute("rel") == "alternate") {
						siteUrl = thisLink.getAttribute("href");
						break;
					}
				}
			} else if(isRSS()) {
				siteUrl = links.item(0).getFirstChild().getNodeValue();
			}
			return siteUrl;
		}
	};
};

var Profile = function(userName:string) {
	
	var getProfileDocument:NotesDocument = function() {
		return Syndicate().getDocumentByPrimaryKey(database, userName);
	};
	
	var profileDocument:NotesDocument = getProfileDocument();
	if (profileDocument.isNewNote() && (userName == context.getUser().getDistinguishedName())) {
		profileDocument.replaceItemValue("Form", "userProfile");
		profileDocument.getFirstItem("primaryKey").setAuthors(true);
		profileDocument.save();
	}
	
	var getProfileItem:string = function(itemName:string) {
		return getProfileDocument().getItemValueString(itemName);
	};
	
	var getNameObject:NotesName = function() {
		return session.createName(userName);
	};
	
	var getDefaultPhoto:string = function() {
		var url:XSPUrl = context.getUrl();
		var baseUrl = "http://" + url.getHost() + "/oneuiv2/images/";
		return {
			full: baseUrl + "profileNoPhoto.png",
			thumb: baseUrl + "thumbNoPhoto.png"
		}
	};
		
	var profile = {
		follow: function() {
			var updated:boolean = false;
			var myProfileDoc:NotesDocument = Syndicate().getMyProfile().getDocument();
			if (myProfileDoc.hasItem("following")) {
				var following:NotesItem = myProfileDoc.getFirstItem("following");
				if (!(following.containsValue(userName))) {
					following.appendToTextList(userName);
					updated = true;
				}
			} else {
				myProfileDoc.replaceItemValue("following", userName);
				updated = true;
			}
			if (updated) {
				myProfileDoc.save();
			}
		},
		getCommonName: function() {
			return getNameObject().getCommon();
		},
		getDocument: function() {
			return getProfileDocument();
		},
		getEmail: function() {
			return getProfileItem("email");
		},
		getJobTitle: function() {
			return getProfileItem("jobTitle");
		},
		getName: function() {
			return userName;
		},
		getPhotoThumbnailUrl: function() {
			return (getProfileItem("photoThumb") || "/thumbNoPhoto.png");
		},
		getPhotoUrl: function() {
			return (getProfileItem("photo") || "/profileNoPhoto.png");
		},
		isFollowedBy: function(followUser:string) {
			return new Profile(followUser).isFollowing(profile.getName());
		},
		isFollowing: function(followUser:string) {
			var result:boolean = false;
			if (profileDocument.hasItem("following")) {
				var following:NotesItem = getProfileDocument().getFirstItem("following");
				result = following.containsValue(followUser);
			}
			return result;
		}
	};
	
	return profile;
	
};

var Subscription = function(unid) {
	var subscriptionUnid:string = unid;
	var getDocument = function() {
		if (subscriptionUnid != null) {
		return database.getDocumentByUNID(subscriptionUnid);
		}
	};
	
	return {
		getAlias: function() {
			return getDocument().getItemValueString("subscriptionFeedAlias");
		},
		getCachedEntries: function() {
			return database.getView("subscriptionFeedEntries").getAllEntriesByKey(context.getUser().getDistinguishedName() + getDocument().getItemValueString("subscriptionFeedURL"), true);
		},
		getCategory: function() {
			return getDocument().getItemValueString("subscriptionCategory");
		},
		getFeed: function() {
			return new Feed(getDocument().getItemValueString("subscriptionFeedUrl"));
		},
		getUnreadCount: function() {
			var entries:NotesViewEntryCollection = this.getCachedEntries();
			var unreadCount:int = 0;
			var entry:NotesViewEntry = entries.getFirstEntry();
			while (entry != null) {
				if (!(entry.getRead(context.getUser().getDistinguishedName()))) {
					unreadCount++;
				}
				entry = entries.getNextEntry(entry);
			}
			return unreadCount;
		},
		load: function() {
			Syndicate().loadSubscription(subscriptionUnid);
		},
		markAllRead: function() {
			return this.getCachedEntries().markAllRead(context.getUser().getDistinguishedName());
		},
		rename: function(newName:string) {
			var docSubscription:NotesDocument = getDocument();
			docSubscription.replaceItemValue("subscriptionFeedAlias", newName);
			docSubscription.save();
		}
	};
};

var Syndicate = function() {
	var globalControllerKey = "com.sandvik.xsp.syndicate.globalController";
	if (!(viewScope.containsKey(globalControllerKey))) {
		var hashCache:java.util.HashMap = new java.util.HashMap();
		var profileCache:java.util.HashMap = new java.util.HashMap();
		
		var autoCache = function(cacheMap:java.util.HashMap, cacheKey:string, callback:Function) {
			if (!(cacheMap.containsKey(cacheKey))) {
				cacheMap.put(cacheKey, callback());
			}
			return cacheMap.get(cacheKey);
		};
		
		var syndicate = {
			addSubscription: function(feedUrl:string, feedCategory:string, loadImmediately:boolean) {
				var newFeed = new Feed(feedUrl);
				var subscriber = context.getUser().getDistinguishedName();
				var feedTitle:string = newFeed.getTitle();
				var subscription:NotesDocument = this.getDocumentByPrimaryKey(database, subscriber + feedUrl);
				subscription.replaceItemValue("Form", "Subscription");
				subscription.replaceItemValue("subscriptionFeedTitle", feedTitle);
				subscription.replaceItemValue("subscriptionFeedAlias", feedTitle);
				subscription.replaceItemValue("subscriptionFeedUrl", feedUrl);
				subscription.replaceItemValue("subscribeDate", session.createDateTime(@Now()));
				subscription.replaceItemValue("subscriptionCategory", feedCategory);
				subscription.replaceItemValue("subscriber", subscriber);
				subscription.save();
				if (loadImmediately) {
					this.loadSubscription(subscription.getUniversalID());
				}
			},
			getContextUser: function () {
				if (!(sessionScope.containsKey("contextUser"))) {
					sessionScope.put("contextUser", context.getUser().getDistinguishedName());
				}
				return sessionScope.get("contextUser");
			},
			getDatabaseUrl: function() {
				var url:XSPUrl = context.getUrl();
				return url.getAddress().replace(url.getSiteRelativeAddress(context),"");
			},
			getDocumentByPrimaryKey: function(dbFrom:NotesDatabase, primaryKey:string) {
				if (!primaryKey) {
					return null;
				}
				var unid:string = "";
				var docTarget:NotesDocument = null;
				try {
					unid = this.getHash(primaryKey);
					docTarget = dbFrom.getDocumentByUNID(unid);
				} catch(e) {
					print("Error retrieving document by primary key " + primaryKey + ": " + e.toString());
					docTarget = dbFrom.createDocument();
					docTarget.setUniversalID(unid);
					var created:NotesDateTime = session.createDateTime("Today");
					created.setNow();
					docTarget.replaceItemValue("created", created);
					docTarget.replaceItemValue("primaryKey", primaryKey);
				}
				return docTarget;
			},
			getHash: function(clearText:string) {
				if (!(hashCache.containsKey(clearText))) {
					var formula:string = "@Middle(@Password(\"" + clearText + "\"); \"(\"; \")\")";
					hashCache.put(clearText, session.evaluate(formula).firstElement().toString());
				}
				return hashCache.get(clearText);
			},
			getMyProfile: function() {
				return this.getProfile(context.getUser().getDistinguishedName());
			},
			getMySubscriptions: function() {
				var subscriptions = [];
//				var myName:string = context.getUser().getDistinguishedName();
				var vwSubscriptions:NotesView = database.getView("vwSubscriptionsBySubscriber");
				var dcSubscriptions:NotesDocumentCollection = vwSubscriptions.getAllDocumentsByKey(syndicate.getContextUser(), true);
				var docSubscription:NotesDocument = dcSubscriptions.getFirstDocument();
				while (docSubscription != null) {
					subscriptions.push(new Subscription(docSubscription.getUniversalID()));
					docSubscription = dcSubscriptions.getNextDocument(docSubscription);
				}
				return subscriptions;
			},
			getProfile: function(userName:string) {
				return autoCache(profileCache, userName, function() {
					return new Profile(userName);
				});
			},
			getProfileName: function() {
				if (!(viewScope.containsKey("profileUserName"))) {
					viewScope.put("profileUserName", param.id);
				}
				return viewScope.get("profileUserName");
			},
			getSubscription: function(url:string) {
				var subscription = null;
				var subscriber:string = context.getUser().getDistinguishedName();
				var subscriptionDoc:NotesDocument = database.getView("subscriptions").getDocumentByKey(subscriber + url, true);
				if (subscriptionDoc != null) {
					subscription = new Subscription(subscriptionDoc.getUniversalID());
				}
				return subscription;
			},
			loadDefaultSubscription: function() {
				var subscription:NotesDocument = database.getView("vwSubscriptionsBySubscriber").getDocumentByKey(syndicate.getContextUser(), true);
				if (subscription != null) {
					this.loadSubscription(subscription.getUniversalID());
				}
			},
			loadSubscription: function(unid:string) {
				viewScope.selectedSubscription = unid;
				viewScope.selectedCollectionType = "subscription";
				try {
					new Subscription(unid).getFeed().cacheEntries();
				} catch (e) {
					
				}
			},
			loadTag: function(tag:string) {
				viewScope.selectedSubscription = tag;
				viewScope.selectedCollectionType = "tag";
			},
			setContextUser: function(userName:string) {
				sessionScope.put("contextUser", userName);
			},
			unsubscribe: function(subscription:NotesDocument) {
				var feedUnid = subscription.getUniversalID();
				subscription.remove(true);
				if (viewScope.selectedSubscription == feedUnid) {
					this.loadDefaultSubscription();
				}
			}
		};
		viewScope.put(globalControllerKey, syndicate);
	}
	return viewScope.get(globalControllerKey);
};


